﻿using System;

using Sce.Pss.Core.Graphics;

using Storyteller.Framework.Graphics;
using System.Diagnostics;
using System.Collections.Generic;

namespace Storyteller.Framework
{
	public abstract class Game : IDisposable
	{
		internal static Game Instance { get; private set; }
		
		internal GraphicsContext GraphicsContext { get; private set; }
		internal Sce.Pss.Core.Input.GamePadData GamePadData { get; set; }
		
		public int ScreenWidth { get; private set; }
		public int ScreenHeight { get; private set; }

		public Game()
		{
			if (Instance != null)
				throw new InvalidOperationException("Cannot instantiate multiple Game objects");

			Instance = this;
			
			GraphicsContext = new GraphicsContext();
			
			ScreenWidth = GraphicsContext.Screen.Width;
			ScreenHeight = GraphicsContext.Screen.Height;
		}
		
		internal void Register(SpriteBatch spriteBatch)
		{
			spriteBatchReferences.Add(new WeakReference(spriteBatch));
		}
		
		public void Run()
		{
			LoadContent();
			
			stopwatch.Start();			
			var previousTime = stopwatch.ElapsedMilliseconds;
			
			// todo: we need a way to quit
			while(true)
			{
				var currentTime = stopwatch.ElapsedMilliseconds;
				float deltaSeconds = (currentTime - previousTime) / 1000.0f;
				previousTime = currentTime;
				
				Sce.Pss.Core.Environment.SystemEvents.CheckEvents();
				GamePadData = Sce.Pss.Core.Input.GamePad.GetData(0);
				Update(deltaSeconds);
				DrawPSS();
			}
			
			UnloadContent();
		}

		public void Clear(Color color)
		{
			GraphicsContext.SetClearColor(color.R, color.G, color.B, color.A);
			GraphicsContext.Clear(ClearMask.Color);
		}
		
		protected void DrawPSS()
		{
			for(int i = spriteBatchReferences.Count - 1; i >= 0; i--)
			{
				object target = spriteBatchReferences[i].Target;
				
				if(target != null)
				{
					(target as SpriteBatch).BeginFrame();
				}
				else
				{
					spriteBatchReferences.RemoveAt(i);
				}
			}
			
			// notify of begin frame
			Draw();
			GraphicsContext.SwapBuffers();
		}

		protected virtual void LoadContent() { }
		protected virtual void UnloadContent() { }

		protected virtual void Update(float deltaSeconds) { }
		protected virtual void Draw(){ }

		protected virtual void ChangeResolution(int width, int height)
		{
			ScreenWidth = width;
			ScreenHeight = height;

//			GL.Viewport(0, 0, width, height);
		}
		
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!disposed)
			{
				if (disposing)
				{
					// dispose of managed resources
				}

				// dispose of unmanaged resources
				disposed = true;
			}
		}
		
		private List<WeakReference> spriteBatchReferences = new List<WeakReference>();
		
		bool disposed = false;
		
		Stopwatch stopwatch = new Stopwatch();
	}
}
